Skip to content

Vitepress 视觉过渡组件

🕐2025-02-16🌱前端 🏷博客建站🏷前端

官方文档中实现了视觉过渡效果,见 扩展默认主题 | VitePress

但是它的实现与布局混在了一起,且没有传递插槽,所以本文实现了这两点

  • 将 ViewTrans.vue 组件剥离出来
  • 传递所有插槽

组件 .vitepress/theme/components/ViewTrans.vue 源码为

vue
<script setup lang="ts">
import { useData } from 'vitepress'
import { nextTick, provide } from 'vue'

const { isDark } = useData()

const enableTransitions = () =>
  'startViewTransition' in document &&
  window.matchMedia('(prefers-reduced-motion: no-preference)').matches

provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
  if (!enableTransitions()) {
    isDark.value = !isDark.value
    return
  }

  const clipPath = [
    `circle(0px at ${x}px ${y}px)`,
    `circle(${Math.hypot(
      Math.max(x, innerWidth - x),
      Math.max(y, innerHeight - y)
    )}px at ${x}px ${y}px)`
  ]

  await document.startViewTransition(async () => {
    isDark.value = !isDark.value
    await nextTick()
  }).ready

  document.documentElement.animate(
    { clipPath: isDark.value ? clipPath.reverse() : clipPath },
    {
      duration: 300,
      easing: 'ease-in',
      pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
    }
  )
})
</script>

<template>
  <slot />
</template>

<style>
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-old(root),
.dark::view-transition-new(root) {
  z-index: 1;
}

::view-transition-new(root),
.dark::view-transition-old(root) {
  z-index: 9999;
}

.VPSwitchAppearance {
  width: 22px !important;
}

.VPSwitchAppearance .check {
  transform: none !important;
}
</style>

.vitepress/theme/index.ts 中注册全局组件

ts
import ViewTrans from "./components/ViewTrans.vue"

export default {

  Layout: () => {
    // return h(DefaultTheme.Layout, null, {
    return h(MyLayout, null, {
      // 插槽配置
    })
  },

  async enhanceApp({ app, router, siteData }) {
    // 注册全局组件
    app.component('ViewTrans', ViewTrans)
  },

} satisfies Theme

MyLayout.vue 中引入,注意要传递插槽,否则 index.ts 中的插槽配置无法启用,
布局组件 .vitepress/layouts/MyLayout.vue 源码为

vue
<script setup lang="ts">
import DefaultTheme from 'vitepress/theme'
</script>

<template>
  <!-- 视觉过渡组件 -->
  <ViewTrans>
    <DefaultTheme.Layout>
      <!-- 传递所有插槽 -->
      <template v-for="(_, name) in $slots" #[name]="slotData">
        <slot :name="name" v-bind="slotData || {}" />
      </template>
    </DefaultTheme.Layout>
  </ViewTrans>
</template>
本站访客数 242 人次 本站总访问量 426